/*
**************************************************************************************
*Myriad Engine                                                                       *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
package leola.lang;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import leola.backend.interpreter.Callstack;
import leola.backend.interpreter.Scope;
import leola.backend.interpreter.SymbolStack;
import leola.frontend.Leola;
import leola.frontend.LeolaLibrary;
import leola.types.LeoArray;
import leola.types.LeoFunction;
import leola.types.LeoNativeClass;
import leola.types.LeoNull;
import leola.types.LeoObject;

/**
 * The system core functions
 * 
 * @author Tony
 *
 */
public class SystemLeolaLibrary implements LeolaLibrary {

	/**
	 * Object
	 */
	private static final Object mutex = new Object();
	
	/**
	 * Leola
	 */
	private static Leola leola;
	
	/* (non-Javadoc)
	 * @see leola.frontend.LeolaLibrary#init(leola.frontend.Leola)
	 */
	@Override
	public void init(Leola aLeola) throws Exception {
		synchronized (mutex) {		
			if ( leola == null ) {
				leola = aLeola;
				leola.getInterpreter().loadClass(SystemLeolaLibrary.class);
			}		
		}
	}
	
	/**
	 * Loads a library
	 * 
	 * @param lib
	 */
	public static final void loadLibrary(String lib) throws Exception {
		leola.loadLibrary(Class.forName(lib));
	}
	
	/**
	 * Loads a class
	 * 
	 * @param className
	 * @throws Exception
	 */
	public static final void include(String className) throws Exception {
		try {
			leola.getInterpreter().loadClass(className);
		}
		catch(Exception e) {
			java.lang.System.err.println(e);
		}
	}
	
	/**
	 * Reads a line from sys in
	 * 
	 * TODO - move to io
	 * @return
	 */
	public static final String readln() {
		String result = null;
		Scanner s = new Scanner(java.lang.System.in);
		try {
			if ( s.hasNext() ) {
				result = s.nextLine();
			}
		}
		finally {
			s.close();
		}
		
		return result;
	}
	
	/**
	 * Prints to the console with a new line
	 * 
	 * @param x
	 */
	public static final void println(Object x) {
		java.lang.System.out.println(x);
	}
	
	/**
	 * Prints to the console.
	 * 
	 * @param x
	 */
	public static final void print(Object x) {
		java.lang.System.out.print(x);
	}
	
	/**
	 * Transforms the supplied object into a number
	 * @param x
	 * @return
	 */
	public static final double toNumber(Object x) {
		String str = x.toString();
		return Double.parseDouble(str);
	}
	
	/**
	 * Transforms the supplied object into a string
	 * @param x
	 * @return
	 */
	public static final String toString(Object x) {
		return x.toString();
	}
	
	public static final LeoNativeClass toBytes(String str) {
		return new LeoNativeClass(byte[].class, str.getBytes());
	}
	
	/**
	 * returns the length of the array
	 * 
	 * @param array
	 * @return
	 */
	public static final int length(LeoArray array) {
		return array.size();
	}
	
	/**
	 * Constructs a new Array
	 * @param size
	 * @return
	 */
	public static final LeoArray newArray(int size) {
		List<LeoObject> array = new ArrayList<LeoObject>(size);
		for(int i = 0; i < size; i++ ) {
			array.add(LeoNull.LEONULL);
		}
		return new LeoArray(array);
	}
	
	/**
	 * Iterates through the array invoking the call back.
	 * 
	 * @param array
	 * @param function
	 * @throws Exception
	 */
	public static final void foreach(LeoArray array, LeoFunction function) throws Exception {
		List<LeoObject> list = array.getArray();
		int size = list.size();
		
		Callstack stack = leola.getInterpreter().getCallstack();
		SymbolStack symStack = leola.getInterpreter().getSymbolStack();
		Scope functionScope = new Scope(true);
		symStack.push(functionScope);
		
		for(int i = 0; i < size; i++) {
			int stackSize = stack.size();
			//stack.push(list.get(i));
			leola.getInterpreter().executeFunction(function, list.get(i));	
			
			/* If there is an object put on the stack
			 * lets check for 'break'
			 */
			if ( stack.size() > stackSize ) {
				LeoObject obj = stack.pop();
				if ( obj.isTrue() ) {
					break;
				}
			}
		}
		
		symStack.pop();
	}
	
	/**
	 * Returns a new thread with the supplied {@link LeoFunction} serving
	 * as the thread runnable.
	 * 
	 * @param function
	 * @return
	 */
	public static final LeoNativeClass newThread(final LeoFunction function) {
		Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {					
					leola.getInterpreter().executeFunction(function);
				} 
				catch (Throwable t) {
					t.printStackTrace();
				}	
			}
		});
		
		return new LeoNativeClass(thread);
	}
	
	/**
	 * Sleeps
	 * 
	 * @param time
	 * @throws Exception
	 */
	public static final void sleep(long time) throws Exception {
		Thread.sleep(time);
	}
	
	/**
	 * Exits the JVM
	 * 
	 * @param code
	 * @throws Exception
	 */
	public static final void exit(int code) throws Exception {
		java.lang.System.exit(code);
	}
}
